/**
 * Copyright 2012-2020 The Feign Authors
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.rollway.restful.utils;


import com.rollway.restful.RestfulClientFactoryBean;
import org.springframework.beans.InvalidPropertyException;
import org.springframework.core.env.Environment;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.Map;

import static java.lang.String.format;

public class Util {

    public static final String DEFAULT = "default";
    public static final String MULTIPART_FORM_DATA = "multipart/form-data";
    public static final String MULTIPART_MIXED = "multipart/mixed";
    public static final String MULTIPART_RELATED = "multipart/related";

    private Util() { // no instances
    }

    /**
     * Copy of {@code com.google.common.base.Preconditions#checkState}.
     */
    public static void checkState(boolean expression,
                                  String errorMessageTemplate,
                                  Object... errorMessageArgs) {
        if (!expression) {
            throw new IllegalStateException(
                    format(errorMessageTemplate, errorMessageArgs));
        }
    }

    /**
     * Identifies a method as a default instance method.
     */
    public static boolean isDefault(Method method) {
        final int SYNTHETIC = 0x00001000;
        return ((method.getModifiers()
                & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC | SYNTHETIC)) == Modifier.PUBLIC)
                && method.getDeclaringClass().isInterface();
    }


    public static String emptyToNull(String string) {
        return string == null || string.isEmpty() ? null : string;
    }

    public static String valNoNull(Object val) {
        return val == null ? "" : val.toString();
    }

    public static String valNoNullWithSep(Object val) {
        if (val == null || !StringUtils.hasText(val.toString())) {
            return "";
        }
        if (val.toString().startsWith("/")) {
            return val.toString();
        }
        return "/" + val;
    }


    public static String configKey(Class targetType, Method method) {
        StringBuilder builder = new StringBuilder();
        builder.append(targetType.getSimpleName());
        builder.append('#').append(method.getName()).append('(');
        for (Type param : method.getGenericParameterTypes()) {
            param = Types.resolve(targetType, targetType, param);
            builder.append(Types.getRawType(param).getSimpleName()).append(',');
        }
        if (method.getParameterTypes().length > 0) {
            builder.deleteCharAt(builder.length() - 1);
        }
        return builder.append(')').toString();
    }


    public static void fillFields(Environment environment, Object target, Map.Entry<String, Object> entry) {
        try {
            Field field = target.getClass().getDeclaredField(entry.getKey());
            ReflectionUtils.makeAccessible(field);
            Object value = resolve(environment, entry.getValue());
            field.set(target, value);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new InvalidPropertyException(RestfulClientFactoryBean.class, entry.getKey(),
                    "Field is not accessible", e);
        }
    }

    public static Object resolve(Environment environment, Object value) {
        if (value != null && value instanceof String) {
            String val = (String) value;
            if (StringUtils.hasText(val)) {
                return environment.resolvePlaceholders(val);
            }
        }
        return value;
    }

    public static boolean isValid(Object val) {
        return isValidString(val) || isValidInteger(val);
    }

    public static boolean isValidString(Object val) {
        return val instanceof String && StringUtils.hasText(val.toString());
    }

    public static boolean isValidInteger(Object val) {
        return val instanceof Integer && (Integer) val != -1;
    }


}
